package com.example.demo20220704.controller;

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

/**
 * @Description:  这个锁可以用于处理幂等性（重复上架同一个秒杀商品）
 * @Author WuGang
 * @Date 2022/7/5 13:36
 * @Version 1.0
 */
@RequestMapping(value = "/redis")
@RestController
public class RedisLockController {
    @Autowired
    RedissonClient redissonClient;

    @GetMapping(value = "/lock")
    public void uploadSeckillSku() {

        // 1.获取互斥锁,名字自定义，不是看到get就以为Redis必须有这个值
        RLock lock = redissonClient.getLock("suomingsuibian4");
        // 10s后自动释放锁
        lock.lock(10, TimeUnit.SECONDS);
        try {
            // 2.执行业务 这里面的业务第二个请求过来执行  或者第二个线程过来执行  如果上面锁还没释放还没到时间就一直请求等待的状态
            System.out.println("执行业务开始");
//            如果锁到期了而业务没有执行完成，锁依然会释放，下一个请求过来依然可以进入该业务，所以自动过期时间最好设置长，然后在finally里面释放，亲测
//            Thread.sleep(20000);
            System.out.println("执行业务结束");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 3.无论业务成功与否，一定要释放锁，即便上面设置自动释放，因为你有可能业务执行完了然而上面设置的自动释放时间还很久那就不行
            /**
             * 线程1锁没有释放，线程二也进来等待线程1释放锁，但是线程二被突然终止，于是它会执行释放他两共同的锁，于是抛异常，
             * 并且这里线程二假释放了线程1的锁自己却把同步方法执行完了
             *      attempt to unlock lock, not locked by current thread by node id: 83ba4b09-d68c-4771-9b6c-f07cce90a34a thread-id: 129
             */
            if (lock != null && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }
}

